using System.ComponentModel;
using System.Runtime.Serialization;

using SharpGL.Enumerations;

namespace SharpGL.OpenGLAttributes
{
    /// <summary>
    /// This class has all the settings you can edit for fog.
    /// </summary>
    [TypeConverter(typeof(System.ComponentModel.ExpandableObjectConverter))]
    [DataContract(/*IsReference = true, */Name = "StencilBufferAttributes", Namespace = "SharpGL.SceneGraph")]
    public class StencilBufferAttributes : OpenGLAttributes
    {
        #region Private Consts

        private static readonly AttributeMask attributeMask = AttributeMask.StencilBuffer;

        #endregion Private Consts

        #region Private Fields

        [DataMember()]
        private bool? enableStencilTest;
        [DataMember()]
        private StencilFunction? stencilFunction;
        [DataMember()]
        private int? stencilReference;
        [DataMember()]
        private uint? stencilMask;
        [DataMember()]
        private int? stencilClearIndex;
        [DataMember()]
        private uint? stencilWriteMask;
        [DataMember()]
        private StencilOperation? operationFail;
        [DataMember()]
        private StencilOperation? operationDepthFail;
        [DataMember()]
        private StencilOperation? operationDepthPass;

        #endregion Private Fields

        #region Protected Methods

        protected override void OnDeserializing()
        {
            AttributeFlags = attributeMask;
        }

        #endregion Protected Methods

        #region Public Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="StencilBufferAttributes"/> class.
        /// </summary>
        public StencilBufferAttributes()
        {
            AttributeFlags = attributeMask;
        }

        #endregion Public Constructors

        #region Public Methods

        /// <summary>
        /// Returns true if any attributes are set.
        /// </summary>
        /// <returns>
        /// True if any attributes are set
        /// </returns>
        public override bool AreAnyAttributesSet()
        {
            return
                enableStencilTest.HasValue ||
                stencilFunction.HasValue ||
                stencilReference.HasValue ||
                stencilMask.HasValue ||
                stencilClearIndex.HasValue ||
                stencilWriteMask.HasValue ||
                operationFail.HasValue ||
                operationDepthFail.HasValue ||
                operationDepthPass.HasValue;
        }

        /// <summary>
        /// Reset all attributes to unset state.
        /// </summary>
        public override void ResetAllAttributes()
        {
            enableStencilTest = null;
            stencilFunction = null;
            stencilReference = null;
            stencilMask = null;
            stencilClearIndex = null;
            stencilWriteMask = null;
            operationFail = null;
            operationDepthFail = null;
            operationDepthPass = null;
        }

        /// <summary>
        /// Sets the attributes.
        /// </summary>
        /// <param name="gl">The OpenGL instance.</param>
        public override void SetAttributes(OpenGL gl)
        {
            if (enableStencilTest.HasValue) gl.EnableIf(OpenGL.GL_STENCIL_TEST, enableStencilTest.Value);
            if (stencilFunction.HasValue && stencilReference.HasValue && stencilMask.HasValue)
                gl.StencilFunc(stencilFunction.Value, stencilReference.Value, stencilMask.Value);
            if (stencilClearIndex.HasValue) gl.ClearStencil(stencilClearIndex.Value);
            if (stencilWriteMask.HasValue) gl.StencilMask(stencilWriteMask.Value);
            if (operationFail.HasValue && operationDepthFail.HasValue && operationDepthPass.HasValue)
                gl.StencilOp(operationFail.Value, operationDepthFail.Value, operationDepthPass.Value);
        }

        #endregion Public Methods

        #region Public Properties

        /// <summary>
        /// Gets or sets the enable stencil test.
        /// </summary>
        /// <value>
        /// The enable stencil test.
        /// </value>
        [Description("EnableStencilTest"), Category("Stencil Buffer")]
        public bool? EnableStencilTest
        {
            get { return enableStencilTest; }
            set
            {
                if (enableStencilTest != value)
                {
                    enableStencilTest = value;

                    NotifyPropertyChanged("EnableStencilTest");
                }
            }
        }

        /// <summary>
        /// Gets or sets the stencil function.
        /// </summary>
        /// <value>
        /// The stencil function.
        /// </value>
        [Description("StencilFunction"), Category("Stencil Buffer")]
        public StencilFunction? StencilFunction
        {
            get { return stencilFunction; }
            set
            {
                if (stencilFunction != value)
                {
                    stencilFunction = value;

                    NotifyPropertyChanged("StencilFunction");
                }
            }
        }

        /// <summary>
        /// Gets or sets the stencil reference.
        /// </summary>
        /// <value>
        /// The stencil reference.
        /// </value>
        [Description("StencilReference"), Category("Stencil Buffer")]
        public int? StencilReference
        {
            get { return stencilReference; }
            set
            {
                if (stencilReference != value)
                {
                    stencilReference = value;

                    NotifyPropertyChanged("StencilReference");
                }
            }
        }

        /// <summary>
        /// Gets or sets the stencil mask.
        /// </summary>
        /// <value>
        /// The stencil mask.
        /// </value>
        [Description("StencilMask"), Category("Stencil Buffer")]
        public uint? StencilMask
        {
            get { return stencilMask; }
            set
            {
                if (stencilMask != value)
                {
                    stencilMask = value;

                    NotifyPropertyChanged("StencilMask");
                }
            }
        }

        /// <summary>
        /// Gets or sets the index of the stencil clear.
        /// </summary>
        /// <value>
        /// The index of the stencil clear.
        /// </value>
        [Description("StencilClearIndex"), Category("Stencil Buffer")]
        public int? StencilClearIndex
        {
            get { return stencilClearIndex; }
            set
            {
                if (stencilClearIndex != value)
                {
                    stencilClearIndex = value;

                    NotifyPropertyChanged("StencilClearIndex");
                }
            }
        }

        /// <summary>
        /// Gets or sets the stencil write mask.
        /// </summary>
        /// <value>
        /// The stencil write mask.
        /// </value>
        [Description("StencilWriteMask"), Category("Stencil Buffer")]
        public uint? StencilWriteMask
        {
            get { return stencilWriteMask; }
            set
            {
                if (stencilWriteMask != value)
                {
                    stencilWriteMask = value;

                    NotifyPropertyChanged("StencilWriteMask");
                }
            }
        }

        /// <summary>
        /// Gets or sets the operation fail.
        /// </summary>
        /// <value>
        /// The operation fail.
        /// </value>
        [Description("OperationFail"), Category("Stencil Buffer")]
        public StencilOperation? OperationFail
        {
            get { return operationFail; }
            set
            {
                if (operationFail != value)
                {
                    operationFail = value;

                    NotifyPropertyChanged("OperationFail");
                }
            }
        }

        /// <summary>
        /// Gets or sets the operation depth pass.
        /// </summary>
        /// <value>
        /// The operation depth pass.
        /// </value>
        [Description("OperationDepthFail"), Category("Stencil Buffer")]
        public StencilOperation? OperationDepthFail
        {
            get { return operationDepthFail; }
            set
            {
                if (operationDepthFail != value)
                {
                    operationDepthFail = value;

                    NotifyPropertyChanged("OperationDepthFail");
                }
            }
        }

        /// <summary>
        /// Gets or sets the operation depth pass.
        /// </summary>
        /// <value>
        /// The operation depth pass.
        /// </value>
        [Description("OperationDepthPass"), Category("Stencil Buffer")]
        public StencilOperation? OperationDepthPass
        {
            get { return operationDepthPass; }
            set
            {
                if (operationDepthPass != value)
                {
                    operationDepthPass = value;

                    NotifyPropertyChanged("OperationDepthPass");
                }
            }
        }

        #endregion Public Properties
    }
}
